﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RPGProject.LoopSystem;
using System.Threading;
namespace RPGProject.GameWorld.GameObjects
{
    class Projectile : WorldSpecificGameObject
    {
        private static Random random = new Random();
        private double dist;
        private double sqrtTwo = Math.Sqrt(2);
        /// <summary>
        /// Amount of damage the projectile does.
        /// </summary>
        private readonly int damage;
        /// <summary>
        /// Starting x position.
        /// </summary>
        private readonly int startx;
        /// <summary>
        /// Starting Y position.
        /// </summary>
        private readonly int starty;
        /// <summary>
        /// Rise of the projectile.
        /// </summary>
        private readonly int rise;
        /// <summary>
        /// Run of the projectile.
        /// </summary>
        private readonly int run;
        /// <summary>
        /// Range of the projectile.
        /// </summary>
        private readonly int range;
        /// <summary>
        /// Who owns the projectile.
        /// </summary>
        private readonly GameObject owner;
        /// <summary>
        /// Whether the projectile has collided or not.
        /// </summary>
        private bool collided;
        public Projectile(int startx, int starty, char icon, int rise, int run, int damage, int range, String name, GameObject owner, World w)
            : base(startx, starty, icon, ConsoleColor.Green, name, w, "projectile")
        {
            this.owner = owner;
            this.range = range;
            this.collided = false;
            this.rise = run;
            this.run = rise;
            this.damage = damage;
            this.startx = startx;
            this.starty = starty;
        }
        /// <summary>
        /// Listens for collision events so it knows to stop, and attempt to damage the target.
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        public override GameObjectEvent Listen(GameObjectEvent e)
        {
            if (e.Name == "collision")
            {
                if (e.Has("accuracy"))
                {
                    int accuracy = (int)e["accuracy"];
                    if (Projectile.random.Next(0, 101) < accuracy)
                    {
                        GameObjectEvent damage = new GameObjectEvent("damage");
                        damage["damage"] = this.damage;
                        damage["source"] = owner;
                        damage["target"] = e["target"];
                        this.world.Listen(damage);
                    }
                    else
                    {
                        GameObjectEvent miss = new GameObjectEvent("miss");
                        miss["target"] = e["target"];
                        miss["source"] = owner;
                        this.world.Listen(miss);
                    }
                }
                GameObjectEvent remove = new GameObjectEvent("remove");
                remove["source"] = this;
                this.world.Listen(remove);
                this.collided = true;
            }
            return null;
        }
        /// <summary>
        /// Runs through the projectiles motion.
        /// </summary>
        public FuncType RunOnce()
        {
            if (this.collided || (dist >= range && range != -1))
            {
                return FuncType.REMOVE_THIS;
            }
            this.world.Render();
            GameObjectEvent moveEvent = new GameObjectEvent("moveAttempt");
            moveEvent["source"] = this;
            // special cases for cardinal directions
            if (rise == 0)
            {
                moveEvent["xmod"] = this.run / Math.Abs(this.run);
                moveEvent["ymod"] = 0;
                this.dist += 1;
            }
            else if (run == 0)
            {
                moveEvent["xmod"] = 0;
                moveEvent["ymod"] = this.rise / Math.Abs(this.rise);
                this.dist += 1;
            }
            else if (Math.Abs(run) == Math.Abs(rise))
            {
                moveEvent["xmod"] = (int)(this.run / Math.Abs(this.run));
                moveEvent["ymod"] = this.rise / Math.Abs(this.rise);
                this.dist += sqrtTwo;
            }
            moveEvent["accuracy"] = 100 - (int)((dist * dist) / (double)Constants.GetInstance()["AccuracyFactor"]);
            this.world.Listen(moveEvent);

            return FuncType.REPEAT;
        }
        /// <summary>
        /// Starts the projectile off in the first square.
        /// </summary>
        public void Init()
        {
            this["x"] = this.startx + run;
            this["y"] = this.starty + rise;
            GameObjectEvent moveEvent = new GameObjectEvent("moveAttempt");
            moveEvent["xmod"] = 0;
            moveEvent["ymod"] = 0;
            moveEvent["accuracy"] = 100;
            moveEvent["source"] = this;
            this.world.Listen(moveEvent);
            if (!this.collided)
            {
                this.world.PutGameObject(this);
            }
        }
        public override string ToString()
        {
            return base.ToString();
        }
    }
}
